package com.tmt.im.manager.service;

import com.alibaba.fastjson2.JSONObject;
import com.tmt.im.common.enums.AddFriendMode;
import com.tmt.im.common.enums.CustomizedMsgCode;
import com.tmt.im.common.pojo.*;
import com.tmt.im.common.yunxin.IYunXinMsgSender;
import com.tmt.im.jpa.entity.IMFriendApply;
import com.tmt.im.jpa.entity.SystemParam;
import com.tmt.im.jpa.repository.IMFriendApplyRepo;
import com.tmt.im.jpa.repository.SystemParamRepo;
import com.tmt.im.manager.config.beans.CurrentServiceProperties;
import com.tmt.im.manager.controller.vo.*;
import com.tmt.im.manager.helper.IgniteQueryHelper;
import com.tmt.im.manager.helper.PrincipalHelper;
import com.tmt.im.manager.helper.QrCodeHelper;
import com.tmt.im.manager.interfaces.IYunXinInterface;
import com.tmt.im.manager.mapstruct.IMFriendApplyMapStruct;
import com.tmt.im.manager.mapstruct.IMFriendApplyMapStruct1;
import com.tmt.springboot.common.helper.IdSnowFlake;
import com.tmt.springboot.common.restful.ExecuteResult;
import com.tmt.springboot.common.restful.IExecuteResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.ignite.Ignite;
import org.apache.ignite.transactions.Transaction;
import org.apache.ignite.transactions.TransactionConcurrency;
import org.apache.ignite.transactions.TransactionIsolation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.security.Principal;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.time.OffsetDateTime;
import java.util.*;

/**
 * @Description TODO
 * @Author wuyi
 * @Date 2024/8/24 17:42
 * @Version 1.0
 **/
@Slf4j
@Service
public class IMFriendIgniteService extends BaseService {

    private final IYunXinInterface iYunXinInterface;


    private final IYunXinMsgSender iYunXinMsgSender;


    private final CurrentServiceProperties serviceProperties;


    private final SystemParamRepo systemParamRepo;

    private final String apiUrlPrefix;


    private final IMFriendApplyRepo imFriendApplyRepo;

    private final IMFriendApplyMapStruct imFriendApplyMapStruct;

    private final IMFriendApplyMapStruct1 imFriendApplyMapStruct1;

    private final int validDays;

    public IMFriendIgniteService(Ignite ignite,
                                 IMFriendApplyRepo imFriendApplyRepo,
                                 IMFriendApplyMapStruct imFriendApplyMapStruct,
                                 IMFriendApplyMapStruct1 imFriendApplyMapStruct1,
                                 CurrentServiceProperties serviceProperties,
                                 IYunXinInterface iYunXinInterface,
                                 IYunXinMsgSender iYunXinMsgSender,
                                 SystemParamRepo systemParamRepo,
                                 @Value("${api-url-prefix}")
                                 String apiUrlPrefix,
                                 @Value("${friend-apply.valid-days:1}")
                                 int validDays) {
        super(ignite);
        this.serviceProperties = serviceProperties;
        this.iYunXinInterface = iYunXinInterface;
        this.iYunXinMsgSender = iYunXinMsgSender;
        this.apiUrlPrefix = apiUrlPrefix;
        this.validDays = validDays;
        this.imFriendApplyRepo = imFriendApplyRepo;
        this.imFriendApplyMapStruct = imFriendApplyMapStruct;
        this.imFriendApplyMapStruct1 = imFriendApplyMapStruct1;
        this.systemParamRepo = systemParamRepo;
    }

    /**
     * 好友申请
     * 1. 检查目标用户是否允许被申请好友
     * 2. 检查当前用户是否在目标用户的黑名单中
     * 3. 检查是否存在未过期的相同的好友申请。
     *
     * @param vo
     * @param principal
     * @return
     */
    public IExecuteResult<FriendApplyResponse> applyFriend(FriendApplyRequest vo, Principal principal) {
        Long currentUserId = PrincipalHelper.getCurrentUserId(principal);
        assert currentUserId != null;

        if (Objects.equals(vo.getUserId(), currentUserId)) {
            return ExecuteResult.failure("不能加自己为好友", null);
        }

        IMUserPoJo userPoJo = userCache.get(vo.getUserId());
        if (userPoJo == null) {
            return ExecuteResult.failure("好友不存在", null);
        }

        PersonalSettingPoJo settingPoJo = settingCache.get(userPoJo.getSettingId());
        if (!settingPoJo.isEnableAddFriend()) {
            return ExecuteResult.failure("该用户已禁止他人添加好友", null);
        }

        if (Arrays.stream(settingPoJo.getAddFriendModes().split(",")).noneMatch(x -> x.equals(vo.getMode().name()))) {
            return ExecuteResult.failure(MessageFormat.format("该用户已禁止{0}", vo.getMode().getMsg()), null);
        }

        String sql = MessageFormat.format("select * from {0} where userId = ? and creatorId = ?", BlackBillPoJo.CACHE_NAME);
        BlackBillPoJo blackBillPoJo = IgniteQueryHelper.findOneBy(sql, new Object[]{currentUserId, vo.getUserId()},
                BlackBillPoJo.class, blackBillCache);
        if (blackBillPoJo != null) {
            return ExecuteResult.failure("你在该用户黑名单中", null);
        }

        sql = MessageFormat.format("select * from {0} where friendId = ? and creatorId = ?", IMFriendApplyPoJo.CACHE_NAME);
        IMFriendApplyPoJo applyPoJo = IgniteQueryHelper.findOneBy(sql, new Object[]{vo.getUserId(), currentUserId},
                IMFriendApplyPoJo.class, friendApplyCache);
        if (applyPoJo != null) {
            if (applyPoJo.getReviewTime() == null) {
                Timestamp expireTime = Timestamp.from((OffsetDateTime.now().minusDays(validDays)).toInstant());
                if (applyPoJo.getCreateTime().after(expireTime)) {
                    return ExecuteResult.failure("正在申请中", null);
                } else {
                    //过期则删除之。
                    friendApplyCache.remove(applyPoJo.getId());
                }
            } else if (!applyPoJo.isReviewStatus()) {
                //上次申请未通过，则删除上次的记录。
                friendApplyCache.remove(applyPoJo.getId());
            } else {
                //审核通过了
                return ExecuteResult.failure("已经是好友了", null);
            }
        } else {
            //在好友列表中查询一下是否存在vo.getUserId()这位好友。
            sql = MessageFormat.format("select * from {0} where friendId = ? and ownerId = ?", IMFriendPoJo.CACHE_NAME);
            IMFriendPoJo friendPoJo = IgniteQueryHelper.findOneBy(sql, new Object[]{vo.getUserId(), currentUserId},
                    IMFriendPoJo.class, friendCache);
            if (friendPoJo != null) {
                return ExecuteResult.failure("已经是好友了", null);
            }
        }

        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        IMFriendApplyPoJo apply = IMFriendApplyPoJo.builder()
                .id(IdSnowFlake.getId())
                .creatorId(currentUserId)
                .friendId(vo.getUserId())
                .mode(vo.getMode().name())
                .note(vo.getNote())
                .reviewStatus(!settingPoJo.isReviewOnAddingFriend())
                .reviewTime(!settingPoJo.isReviewOnAddingFriend() ? timestamp : null)
                .groupId(vo.getGroupId())
                .createTime(timestamp)
                .insert(true)
                .build();

        LinkedHashMap<IMFriendKey, IMFriendPoJo> friendPoJoMap = null;
        Map<Long, IMFriendGroupMappingPoJo> mappingPoJoMap = new HashMap<>();
        StringBuilder error = new StringBuilder();
        try {
            //如果被申请的用户不需要审核好友申请，则直接同步云信。
            if (!settingPoJo.isReviewOnAddingFriend()) {
                if (!iYunXinInterface.applyFriend(apply.getCreatorId(), apply.getFriendId(), 1, userPoJo.getNickname(), null, error)) {
                    throw new Exception(error.toString());
                }
                //在本地数据库中创建两条好友记录。
                friendPoJoMap = createFriendData(apply, userPoJo, null, mappingPoJoMap);
            } else {
                //调用云信的添加好友接口, 需要对方同意。
                if (!iYunXinInterface.applyFriend(apply.getCreatorId(), apply.getFriendId(), 2, userPoJo.getNickname(), null, error)) {
                    throw new Exception(error.toString());
                }
            }
        } catch (Exception ex) {
            return ExecuteResult.failure(ex.getMessage(), null);
        }

        FriendInfoVo friendInfoVo = null;
        FriendApplyInfoVo applyInfoVo = null;
        try (Transaction tx = ignite.transactions().txStart(TransactionConcurrency.OPTIMISTIC,
                TransactionIsolation.SERIALIZABLE)) {
            friendApplyCache.put(apply.getKey(), apply);
            if (!CollectionUtils.isEmpty(friendPoJoMap)) {
                friendCache.putAll(friendPoJoMap);
                if (!CollectionUtils.isEmpty(mappingPoJoMap)) {
                    groupMappingCache.putAll(mappingPoJoMap);
                }
            }
            tx.commit();
        } catch (Exception ex) {
            return ExecuteResult.failure(ex.getMessage(), null);
        }

        if (settingPoJo.isReviewOnAddingFriend()) {
            //发送好友申请通知。
            sendFriendApplyNotify(currentUserId, vo.getUserId());
        } else {
            //向好友双方发送好友同意通知
            sendFriendAgreeNotify(currentUserId, vo.getUserId());
            //发送第一条聊天记录。
            sendFirstChatData(currentUserId, vo.getUserId());
        }

        if (!CollectionUtils.isEmpty(friendPoJoMap)) {
            IMFriendKey key = friendPoJoMap.keySet().stream().findFirst().orElse(null);
            if (key != null) {
                IMFriendPoJo friendPoJo = friendPoJoMap.get(key);
                friendInfoVo = convertToFriendInfoItem(friendPoJo);
            }
        } else {
            applyInfoVo = convertToFriendApplyInfoItem(apply);
        }

        return ExecuteResult.success(new FriendApplyResponse(applyInfoVo, friendInfoVo));
    }

    /**
     * 审核好友申请
     *
     * @param vo
     * @param principal
     * @return
     */
    //@Transactional(rollbackFor = Exception.class)
    public IExecuteResult<Boolean> reviewApply(FriendApplyReplyVo vo, Principal principal) {
        IMFriendApplyPoJo apply = friendApplyCache.get(vo.getApplyId());
        if (apply == null) {
            return ExecuteResult.failure("好友申请不存在", false);
        }

        if (apply.getReviewTime() != null) {
            return ExecuteResult.failure("禁止重复审核", false);
        }

        Timestamp expireTime = Timestamp.from((OffsetDateTime.now().minusDays(validDays)).toInstant());
        if (!apply.getCreateTime().after(expireTime)) {
            return ExecuteResult.failure("好友申请已过期", false);
        }

        Long currentUserId = PrincipalHelper.getCurrentUserId(principal);
        assert currentUserId != null;
        if (!Objects.equals(currentUserId, apply.getFriendId())) {
            return ExecuteResult.failure("不能审核加他人为好友的申请", false);
        }

        try {
            StringBuilder error = new StringBuilder();
            //直接加为好友
            if (!iYunXinInterface.applyFriend(currentUserId, apply.getCreatorId(), vo.getResult() + 2, null, null, error)) {
                System.out.println("-----applyFriend-----" + error);
            }

            Map<IMFriendKey, IMFriendPoJo> friendPoJoMap = null;
            Map<Long, IMFriendGroupMappingPoJo> mappingPoJoMap = new HashMap<>();
            if (vo.getResult() == 1) {
                //创建两条好友记录。
                IMUserPoJo userPoJo = userCache.get(apply.getFriendId());
                friendPoJoMap = createFriendData(apply, userPoJo, vo.getRemark(), mappingPoJoMap);
            }
            //完善申请记录。
            apply.setReviewNote(vo.getRemark());
            apply.setReviewStatus(vo.getResult() == 1);
            apply.setReviewTime(new Timestamp(System.currentTimeMillis()));
            apply.setInsert(false);

            try (Transaction tx = ignite.transactions().txStart(TransactionConcurrency.OPTIMISTIC,
                    TransactionIsolation.SERIALIZABLE)) {
                if (!CollectionUtils.isEmpty(friendPoJoMap)) {
                    friendCache.putAll(friendPoJoMap);
                    if (!CollectionUtils.isEmpty(mappingPoJoMap)) {
                        groupMappingCache.putAll(mappingPoJoMap);
                    }
                }
                friendApplyCache.put(apply.getKey(), apply);
                tx.commit();
            }

            if (apply.isReviewStatus()) {
                //发送成为好友后的招呼语。
                sendFirstChatData(currentUserId, apply.getCreatorId());
            }

        } catch (Exception ex) {
            return ExecuteResult.failure(ex.getMessage(), false);
        }
        return ExecuteResult.success(true);
    }

    public IExecuteResult<SlideQueryFriendApplyResponse> slideQueryFriendApplyList(SlideQueryRequest request, boolean queryReceived, Principal principal) {
        if (request.getStartId() == null || request.getStartId() == 0) {
            request.setStartId(Long.MAX_VALUE);
        }
        String flag0 = queryReceived ? "memberId" : "creatorId";
        String flag1 = request.getBackward() ? ">" : "<";
        String flag2 = request.getBackward() ? "asc" : "desc";

        Long currentUserId = PrincipalHelper.getCurrentUserId(principal);

        String sql = MessageFormat.format("select * from {0} where reviewTime is null and {1} = ? and id {2} ? order by id {3} limit ?, ?",
                IMFriendApplyPoJo.CACHE_NAME, flag0, flag1, flag2);
        Object[] params = new Object[]{true, currentUserId, request.getStartId(), 0, request.getWindowSize()};
        List<IMFriendApplyPoJo> applyPoJos = IgniteQueryHelper.findAllBy(sql, params, IMFriendApplyPoJo.class, friendApplyCache);
        if (CollectionUtils.isEmpty(applyPoJos)) {
            SlideQueryFriendApplyResponse response = new SlideQueryFriendApplyResponse(new ArrayList<>());
            BeanUtils.copyProperties(request, response);
            return ExecuteResult.success("没有更多的数据了", response);
        }

        if ("desc".equals(flag2)) {
            //如果是向前查询则需要倒序一下，保持升序排序。
            applyPoJos = applyPoJos.stream().sorted(Comparator.comparingLong(IMFriendApplyPoJo::getId)).toList();
        }

        List<FriendApplyInfoVo> list = convertToFriendApplyInfoItemList(applyPoJos);
        SlideQueryFriendApplyResponse response = new SlideQueryFriendApplyResponse(list);
        BeanUtils.copyProperties(request, response);
        return ExecuteResult.success(response);
    }

    @Transactional(readOnly = true)
    public IExecuteResult<PageQueryFriendApplyResponse> pageQueryFriendApplyList(Pageable pageable, boolean queryReceived, Principal principal) {
        Long currentUserId = PrincipalHelper.getCurrentUserId(principal);
        Page<IMFriendApply> friendApplies = queryReceived ? imFriendApplyRepo.findAllByReviewTimeIsNullAndFriendId(currentUserId, pageable)
                : imFriendApplyRepo.findAllByReviewTimeIsNullAndCreatorId(currentUserId, pageable);
        List<FriendApplyInfoVo> voList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(friendApplies.getContent())) {
            //IMFriendApply列表转换为FriendApplyInfoVo对象列表。
            voList = queryReceived ? imFriendApplyMapStruct1.toFriendApplyInfoVos(friendApplies.getContent())
                    : imFriendApplyMapStruct.toFriendApplyInfoVos(friendApplies.getContent());
            String url = "http://" + serviceProperties.getExternalClientService();
            for (FriendApplyInfoVo applyInfoVo : voList) {
                String avatar = url + applyInfoVo.getFriendAvatar();
                applyInfoVo.setFriendAvatar(avatar);
            }
        }

        //构造返回数据。
        PageQueryFriendApplyResponse response = new PageQueryFriendApplyResponse((int) friendApplies.getTotalElements(), voList);
        response.setPageIndex(pageable.getPageNumber());
        response.setPageSize(pageable.getPageSize());
        return ExecuteResult.success(response);
    }

    public IExecuteResult<SlideQueryFriendResponse> slideQueryFriendList(SlideQueryRequest request, Principal principal) {
        long startId = (request.getStartId() == null || request.getStartId() <= 0) ? Long.MAX_VALUE : request.getStartId();

        String flag1 = request.getBackward() ? ">" : "<";
        String flag2 = request.getBackward() ? "asc" : "desc";

        Long currentUserId = PrincipalHelper.getCurrentUserId(principal);

        String sql = MessageFormat.format("select * from {0} where ownerId = ? and mid {1} ? order by mid {2} limit ?, ?",
                IMFriendPoJo.CACHE_NAME, flag1, flag2);
        Object[] params = new Object[]{currentUserId, startId, 0, request.getWindowSize()};
        List<IMFriendPoJo> friendPoJos = IgniteQueryHelper.findAllBy(sql, params, IMFriendPoJo.class, friendCache);
        if (CollectionUtils.isEmpty(friendPoJos)) {
            SlideQueryFriendResponse response = new SlideQueryFriendResponse(new ArrayList<>());
            BeanUtils.copyProperties(request, response);
            return ExecuteResult.success("没有更多的数据了", response);
        }

        if ("desc".equals(flag2)) {
            //如果是向前查询则需要倒序一下，保持升序排序。
            friendPoJos = friendPoJos.stream().sorted(Comparator.comparingLong(IMFriendPoJo::getId)).toList();
        }

        List<FriendInfoVo> list = convertToFriendInfoItemList(friendPoJos);
        SlideQueryFriendResponse response = new SlideQueryFriendResponse(list);
        BeanUtils.copyProperties(request, response);
        return ExecuteResult.success(response);
    }

    /**
     * 按条件查询好友
     *
     * @param request
     * @param principal
     * @return
     */
    public IExecuteResult<SlideConditionQueryFriendResponse> slideQueryFriendByCondition(SlideConditionQueryRequest request, Principal principal) {
        if (!StringUtils.hasText(request.getMemberCode())
                && !StringUtils.hasText(request.getMobile())) {
            return ExecuteResult.failure(404, "查询条件无效", null);
        }

        List<IMUserPoJo> userPoJos = new ArrayList<>();
        if (StringUtils.hasText(request.getMemberCode()) || StringUtils.hasText(request.getMobile())) {
            List<Object> tmpParams = new ArrayList<>();
            String memberCodeCondition = "";
            if (StringUtils.hasText(request.getMemberCode())) {
                memberCodeCondition = " memberCode = ? ";
                tmpParams.add(request.getMemberCode());
            }
            String mobileCondition = "";
            if (StringUtils.hasText(request.getMobile())) {
                mobileCondition = (StringUtils.hasText(memberCodeCondition) ? " or " : " ") + "mobile = ? ";
                tmpParams.add(request.getMobile());
            }
            String sql = MessageFormat.format("select * from {0} where {1}{2}", IMUserPoJo.CACHE_NAME,
                    memberCodeCondition, mobileCondition);
            userPoJos = IgniteQueryHelper.findAllBy(sql, tmpParams.toArray(), IMUserPoJo.class, userCache);
        }

        Long currentUserId = PrincipalHelper.getCurrentUserId(principal);

        List<Object> params = new ArrayList<>();
        params.add(currentUserId);


        StringBuilder memberIds = new StringBuilder();
        for (IMUserPoJo userPoJo : userPoJos) {
            memberIds.append(" or friendId = ?");
            params.add(userPoJo.getId());
        }

        String memberIdsCondition = "";
        if (memberIds.length() > 4) {
            memberIdsCondition = MessageFormat.format("and ({0}) ", memberIds.substring(4));
        }

        long startId = Long.MAX_VALUE;
        if (request.getStartId() != null && request.getStartId() > 0) {
            startId = request.getStartId();
        }
        params.add(startId);
        params.add(0);
        params.add(request.getWindowSize());

        String flag1 = request.getBackward() ? ">" : "<";
        String flag2 = request.getBackward() ? "asc" : "desc";

        String sql = MessageFormat.format("select * from {0} where ownerId = ? and mid {1} ?{2} order by mid {3} limit ?, ?",
                IMFriendPoJo.CACHE_NAME, flag1, memberIdsCondition, flag2);
        List<IMFriendPoJo> friendPoJos = IgniteQueryHelper.findAllBy(sql, params.toArray(), IMFriendPoJo.class, friendCache);
        if (CollectionUtils.isEmpty(friendPoJos)) {
            SlideConditionQueryFriendResponse response = new SlideConditionQueryFriendResponse(new ArrayList<>());
            BeanUtils.copyProperties(request, response);
            return ExecuteResult.success("没有更多的数据了", response);
        }

        if ("desc".equals(flag2)) {
            //如果是向前查询则需要倒序一下，保持升序排序。
            friendPoJos = friendPoJos.stream().sorted(Comparator.comparingLong(IMFriendPoJo::getId)).toList();
        }

        List<FriendInfoVo> list = convertToFriendInfoItemList(friendPoJos);
        SlideConditionQueryFriendResponse response = new SlideConditionQueryFriendResponse(list);
        BeanUtils.copyProperties(request, response);
        return ExecuteResult.success(response);
    }

    /**
     * 更新好友备注
     *
     * @param currentUserId
     * @param vo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public IExecuteResult<Boolean> updateFriend(Long currentUserId, UpdateFriendRequest vo) {
        String sql = MessageFormat.format("select * from {0} where ownerId = ? and friendId = ?",
                IMFriendPoJo.CACHE_NAME);
        IMFriendPoJo friendPoJo = IgniteQueryHelper.findOneBy(sql, new Object[]{currentUserId, vo.getFriendId()},
                IMFriendPoJo.class, friendCache);
        if (friendPoJo == null) {
            return ExecuteResult.failure(MessageFormat.format("指定的好友(ID={0})不存在，或对方不是你的好友", vo.getFriendId()), false);
        }

        StringBuilder error = new StringBuilder();
        if (!iYunXinInterface.updateFriend(currentUserId, vo.getFriendId(), vo.getAlias(), error)) {
            return ExecuteResult.failure(error.toString(), false);
        }

        friendPoJo.setAlias(vo.getAlias());
        friendCache.put(friendPoJo.getKey(), friendPoJo);
        return ExecuteResult.success(true);
    }

    /**
     * 删除好友
     *
     * @param currentUserId
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public IExecuteResult<Boolean> deleteFriend(Long currentUserId, DeleteFriendRequest request) {
        Object[] params = new Object[1 + request.getFriendIds().size()];
        params[0] = currentUserId;
        StringBuilder memberIds = new StringBuilder();
        for (int i = 0; i < request.getFriendIds().size(); i++) {
            params[i + 1] = request.getFriendIds().get(i);
            memberIds.append(" or friendId = ?");
        }
        String sql = MessageFormat.format("select * from {0} where ownerId = ? and ({1})",
                IMFriendPoJo.CACHE_NAME, memberIds.substring(4));
        List<IMFriendPoJo> friendPoJos = IgniteQueryHelper.findAllBy(sql, params, IMFriendPoJo.class, friendCache);
        if (CollectionUtils.isEmpty(friendPoJos) || friendPoJos.size() != request.getFriendIds().size()) {
            return ExecuteResult.failure(request.getFriendIds().size() > 1 ? "部分好友不存在" : "目标好友不存在", false);
        }

        try (Transaction tx = ignite.transactions().txStart(TransactionConcurrency.OPTIMISTIC, TransactionIsolation.SERIALIZABLE)) {
            Long applyId = null;
            StringBuilder error = new StringBuilder();
            if (!request.getDeleteAll()) {
                for (IMFriendPoJo friendPoJo : friendPoJos) {
                    if (!iYunXinInterface.deleteFriend(currentUserId, friendPoJo.getFriendId(), error)) {
                        log.error("------云信接口删除好友失败-----" + error);
                    }
                    //只删除自己的好友，不影响对方的好友列表
                    friendCache.remove(friendPoJo.getKey());
                    //如果好友申请记录关联的好友记录都已经被删除了，则删除该申请记录。
                    applyId = friendPoJo.getFriendApplyId();
                    sql = MessageFormat.format("select * from {0} where friendApplyId = ?", IMFriendPoJo.CACHE_NAME);
                    IMFriendPoJo tmpFriendPoJo = IgniteQueryHelper.findOneBy(sql, new Object[]{applyId}, IMFriendPoJo.class, friendCache);
                    if (tmpFriendPoJo == null) {
                        friendApplyCache.remove(applyId);
                    }
                }
            } else {
                //同时删除双方的好友记录。
                for (IMFriendPoJo friendPoJo : friendPoJos) {
                    //在自己的好友列表中删除对方。
                    if (!iYunXinInterface.deleteFriend(currentUserId, friendPoJo.getFriendId(), error)) {
                        log.error("------云信接口删除好友失败-----" + error);
                    }
                    //在对方的好友列表中删除自己。
                    if (!iYunXinInterface.deleteFriend(friendPoJo.getFriendId(), currentUserId, error)) {
                        log.error("------云信接口删除好友失败-----" + error);
                    }

                    applyId = friendPoJo.getFriendApplyId();
                    sql = MessageFormat.format("select * from {0} where friendApplyId = ?", IMFriendPoJo.CACHE_NAME);
                    List<IMFriendPoJo> tmpFriendPoJos = IgniteQueryHelper.findAllBy(sql, new Object[]{applyId}, IMFriendPoJo.class, friendCache);
                    if (!CollectionUtils.isEmpty(tmpFriendPoJos)) {
                        for (IMFriendPoJo tmpFriendPoJo : tmpFriendPoJos) {
                            friendCache.remove(tmpFriendPoJo.getKey());
                        }
                    }
                    friendApplyCache.remove(applyId);
                }
            }
            tx.commit();
        }

        JSONObject json = new JSONObject();
        json.put("type", CustomizedMsgCode.DEL_FRIEND.getType());
        json.put("msg", "删除好友通知");
        StringBuilder error = new StringBuilder();
        //向好友双方发送通知。
        for (IMFriendPoJo friendPoJo : friendPoJos) {
            json.put("userId", friendPoJo.getFriendId());
            if (!iYunXinMsgSender.sendCustomizedMsg(currentUserId, friendPoJo.getFriendId(), json, error)) {
                log.error("--1---发送删除好友通知失败-----" + error);
            }
            json.put("userId", currentUserId);
            if (!iYunXinMsgSender.sendCustomizedMsg(friendPoJo.getFriendId(), currentUserId, json, error)) {
                log.error("--2---发送删除好友通知失败-----" + error);
            }
        }
        return ExecuteResult.success(true);
    }

    public IExecuteResult<Boolean> pullInBlackBill(Long currentUserId, BlackBillRequest request) {
        String sql = MessageFormat.format("select * from {0} where ownerId = ? and friendId = ?",
                IMFriendPoJo.CACHE_NAME);
        IMFriendPoJo friendPoJo = IgniteQueryHelper.findOneBy(sql, new Object[]{currentUserId, request.getFriendId()},
                IMFriendPoJo.class, friendCache);
        if (friendPoJo == null) {
            return ExecuteResult.failure(MessageFormat.format("指定的好友(ID={0})不存在，或对方不是你的好友", request.getFriendId()), false);
        }

        StringBuilder error = new StringBuilder();
        if (!iYunXinInterface.pullInBlackBill(currentUserId, request.getFriendId(), request.isPullIn(), error)) {
            return ExecuteResult.failure(error.toString(), false);
        }

        friendPoJo.setInBlackBill(request.isPullIn());
        friendCache.put(friendPoJo.getKey(), friendPoJo);
        return ExecuteResult.success(true);
    }

    public IExecuteResult<QueryBlackBillResponse> listBlackBill(Long currentUserId, PageQueryRequest request, Principal principal) {
        String sql = MessageFormat.format("select count(*) from {0} where sessionId is null and creatorId = ? ", BlackBillPoJo.CACHE_NAME);
        long total = IgniteQueryHelper.getCountBy(sql, new Object[]{currentUserId}, blackBillCache);

        sql = MessageFormat.format("select * from {0} where sessionId is null and creatorId = ? order by createTime limit ?, ?", BlackBillPoJo.CACHE_NAME);
        List<BlackBillPoJo> blackBillPoJos = IgniteQueryHelper.findAllBy(sql, new Object[]{currentUserId,
                request.getPageIndex() * request.getPageSize(), request.getPageSize()}, BlackBillPoJo.class, blackBillCache);
        List<BlackBillItem> itemList = new ArrayList<>();
        for (BlackBillPoJo blackBillPoJo : blackBillPoJos) {
            BlackBillItem item = new BlackBillItem();
            itemList.add(item);

            item.setId(blackBillPoJo.getId());
            item.setUserId(blackBillPoJo.getUserId());
            IMUserPoJo userPoJo = userCache.get(item.getUserId());
            if (userPoJo != null) {
                String avatar = "http://" + serviceProperties.getExternalClientService() + userPoJo.getAvatar();
                item.setNickname(userPoJo.getNickname());
                item.setAvatar(avatar);
            }
        }

        QueryBlackBillResponse response = new QueryBlackBillResponse((int) total, itemList);
        BeanUtils.copyProperties(request, response);
        return ExecuteResult.success(response);
    }

    public IExecuteResult<Boolean> enableNoDisturbing(Long currentUserId, NoDisturbingRequest request) {
        String sql = MessageFormat.format("select * from {0} where ownerId = ? and friendId = ?",
                IMFriendPoJo.CACHE_NAME);
        IMFriendPoJo friendPoJo = IgniteQueryHelper.findOneBy(sql, new Object[]{currentUserId, request.getFriendId()},
                IMFriendPoJo.class, friendCache);
        if (friendPoJo == null) {
            return ExecuteResult.failure(MessageFormat.format("指定的好友(ID={0})不存在，或对方不是你的好友", request.getFriendId()), false);
        }

        StringBuilder error = new StringBuilder();
        if (!iYunXinInterface.enableNoDisturbing(currentUserId, request.getFriendId(), request.isNoDisturbing(), error)) {
            return ExecuteResult.failure(error.toString(), false);
        }

        friendPoJo.setNoDisturbing(request.isNoDisturbing());
        friendCache.put(friendPoJo.getKey(), friendPoJo);
        return ExecuteResult.success(true);
    }

    /***********************************以下为私有方法*********************************************/

    private LinkedHashMap<IMFriendKey, IMFriendPoJo> createFriendData(IMFriendApplyPoJo apply, IMUserPoJo friendUser,
                                                                      String remark, Map<Long, IMFriendGroupMappingPoJo> mappingPoJoMap) throws Exception {
        LinkedHashMap<IMFriendKey, IMFriendPoJo> resultMap = new LinkedHashMap<>();
        IMUserPoJo creator = userCache.get(apply.getCreatorId());
        //创建两条好友记录。
        Long tmpId = IdSnowFlake.getId();
        IMFriendPoJo friend1 = IMFriendPoJo.builder()
                .id(tmpId)
                .mid(tmpId)
                .ownerId(apply.getCreatorId())
                .friendId(apply.getFriendId())
                .alias(remark)
                .holdInTop(false)
                .inBlackBill(false)
                .noDisturbing(false)
                .friendApplyId(apply.getId())
                .createTime(new Timestamp(System.currentTimeMillis()))
                .insert(true)
                .build();

        tmpId = IdSnowFlake.getId();
        IMFriendPoJo friend2 = IMFriendPoJo.builder()
                .id(tmpId)
                .mid(tmpId)
                .ownerId(apply.getFriendId())
                .friendId(apply.getCreatorId())
                .alias(creator.getNickname())
                .holdInTop(false)
                .inBlackBill(false)
                .noDisturbing(false)
                .friendApplyId(apply.getId())
                .createTime(new Timestamp(System.currentTimeMillis()))
                .insert(true)
                .build();
        //保存到缓存和数据库中。
        resultMap.put(friend1.getKey(), friend1);
        resultMap.put(friend2.getKey(), friend2);

        //如果设置了分组，则创建好友分组映射记录。
        if (apply.getGroupId() != null && apply.getGroupId() > 0L && mappingPoJoMap != null) {
            //查找之前存在的分组记录。
            String sql = MessageFormat.format("select * from {0} where creatorId = ? and friendId = ? and groupId = ?", IMFriendGroupMappingPoJo.CACHE_NAME);
            IMFriendGroupMappingPoJo mapping = IgniteQueryHelper.findOneBy(sql,
                    new Object[]{creator.getId(), apply.getFriendId(), apply.getGroupId()},
                    IMFriendGroupMappingPoJo.class, groupMappingCache);
            if (mapping == null) {
                mapping = IMFriendGroupMappingPoJo.builder()
                        .id(IdSnowFlake.getId())
                        .friendId(apply.getFriendId())
                        .groupId(apply.getGroupId())
                        .creatorId(creator.getId())
                        .createTime(new Timestamp(System.currentTimeMillis()))
                        .insert(true)
                        .build();
            } else {
                //更新好友分组映射创建时间。
                mapping.setCreateTime(new Timestamp(System.currentTimeMillis()));
                mapping.setInsert(false);
            }
            mappingPoJoMap.put(mapping.getKey(), mapping);
        }
        return resultMap;
    }

    private FriendApplyInfoVo convertToFriendApplyInfoItem(IMFriendApplyPoJo applyPoJo) {
        FriendApplyInfoVo vo = new FriendApplyInfoVo();
        BeanUtils.copyProperties(applyPoJo, vo);
        IMUserPoJo userPoJo = userCache.get(applyPoJo.getFriendId());
        vo.setFriendNickname(userPoJo.getNickname());
        vo.setFriendAvatar(userPoJo.getAvatar());

        if (applyPoJo.getGroupId() != null) {
            IMFriendGroupPoJo groupPoJo = friendGroupCache.get(applyPoJo.getGroupId());
            vo.setGroupName(groupPoJo.getName());
        }

        AddFriendMode mode = Arrays.stream(AddFriendMode.values())
                .filter(x -> x.name().equals(applyPoJo.getMode()))
                .findFirst().orElse(null);
        if (mode != null) {
            vo.setMode(mode);
            vo.setModeDescr(mode.getMsg());
        }
        return vo;
    }

    private List<FriendApplyInfoVo> convertToFriendApplyInfoItemList(List<IMFriendApplyPoJo> applyPoJos) {
        List<FriendApplyInfoVo> list = new ArrayList<>();
        for (IMFriendApplyPoJo applyPoJo : applyPoJos) {
            FriendApplyInfoVo vo = convertToFriendApplyInfoItem(applyPoJo);
            list.add(vo);
        }
        return list;
    }

    private FriendInfoVo convertToFriendInfoItem(IMFriendPoJo friendPoJo) {
        FriendInfoVo vo = new FriendInfoVo();
        BeanUtils.copyProperties(friendPoJo, vo);
        IMUserPoJo userPoJo = userCache.get(friendPoJo.getFriendId());
        vo.setId(userPoJo.getId());
        vo.setNickname(userPoJo.getNickname());
        vo.setMemberCode(userPoJo.getMemberCode());
        vo.setGrade(userPoJo.getGrade() != null ? userPoJo.getGrade() : 0);
        vo.setAvatar("http://" + serviceProperties.getExternalClientService() + userPoJo.getAvatar());
        String url = "http://" + serviceProperties.getExternalClientService() + QrCodeHelper.getQrCodeUrl(apiUrlPrefix, vo.getId());
        vo.setQrCodeUrl(url);
        return vo;
    }

    private List<FriendInfoVo> convertToFriendInfoItemList(List<IMFriendPoJo> friendPoJos) {
        List<FriendInfoVo> list = new ArrayList<>();
        for (IMFriendPoJo friendPoJo : friendPoJos) {
            FriendInfoVo vo = convertToFriendInfoItem(friendPoJo);
            list.add(vo);
        }
        return list;
    }

    private void sendFriendApplyNotify(Long from, Long to) {
        Timestamp expireTime = Timestamp.from((OffsetDateTime.now().minusDays(validDays)).toInstant());
        int friendApplyCount = imFriendApplyRepo.countByReviewTimeIsNullAndFriendIdAndCreateTimeAfter(to, expireTime);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("type", CustomizedMsgCode.FRIEND_APPLY.getType());
        jsonObject.put("num", friendApplyCount); // 查询多少条未处理
        jsonObject.put("msg", "好友申请通知");
        StringBuilder error = new StringBuilder();
        if (!iYunXinMsgSender.sendCustomizedMsg(from, to, jsonObject, error)) {
            log.error("----好友申请通知失败-----" + error);
        }
    }

    private void sendFriendAgreeNotify(Long from, Long to) {
        JSONObject json = new JSONObject();
        json.put("type", CustomizedMsgCode.ADD_FRIENDS.getType());
        json.put("msg", "添加好友成功通知");
        StringBuilder error = new StringBuilder();
        json.put("userId", from);
        if (!iYunXinMsgSender.sendCustomizedMsg(from, to, json, error)) {
            log.error("-----发送添加好友成功通知失败-----" + error);
        }
        json.put("userId", to);
        error.setLength(0);
        if (!iYunXinMsgSender.sendCustomizedMsg(to, from, json, error)) {
            log.error("-----发送添加好友成功通知失败-----" + error);
        }
    }

    private void sendFirstChatData(Long from, Long to) {
        String hello = null;
        SystemParam param = systemParamRepo.findOneByParamCode("first_chat_msg");
        if (param != null) {
            hello = param.getParamValue();
        }
        if (hello == null || !StringUtils.hasText(hello.trim())) {
            hello = "你好";
        }

        JSONObject msgJson = new JSONObject();
        msgJson.put("msg", hello);

        StringBuilder error = new StringBuilder();
        if (!iYunXinMsgSender.sendMsg(from, to, 0, msgJson, error)) {
            System.out.println("----sendMsg-----" + error);
        }
    }
}
